package com.huxi.http;

import java.lang.reflect.Array;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.springframework.http.HttpHeaders;
import org.springframework.web.util.UriUtils;

public class HttpUtils {

	/**
	 * 截断文本长度
	 * 
	 * @param text   文本
	 * @param limit  限制长度
	 * @param append 超长追加标识
	 * @return 截断后文本
	 */
	public static String trancTextLength(String text, int limit, String append) {
		if (text != null && limit > 0 && text.length() > limit) {
			for (; limit < text.length(); limit++) {
				int code = text.codePointAt(limit);
				if (!Character.isUnicodeIdentifierPart(code)) {
					if (Character.isUnicodeIdentifierStart(code)) {
						return text.substring(0, limit - 1) + append;
					} else {
						return text.substring(0, limit) + append;
					}
				}
			}
		}
		return text;
	}

	/**
	 * 字符串URI解码
	 * 
	 * @param text 字符串
	 * @return 解码后结果
	 */
	public static String decodeUri(String text) {
		return text != null ? UriUtils.decode(text, StandardCharsets.UTF_8) : null;
	}

	/**
	 * 字符串URI编码
	 * 
	 * <ul>
	 * <li>[0-9a-zA-Z-_.]字符不编码；</li>
	 * <li>空格转码为 %20；</li>
	 * <li>其它字符都需要转码；</li>
	 * </ul>
	 * 
	 * @param text 字符串
	 * @return 编码后结果
	 */
	public static String encodeUriComponent(String text) {
		return text != null ? UriUtils.encode(text, StandardCharsets.UTF_8) : null;
	}

	/**
	 * 字符串URL编码
	 * <ul>
	 * <li>[0-9a-zA-Z-_./=?&,*] 字符不编码；</li>
	 * <li>空格转码为 %20；</li>
	 * <li>其它字符都需要转码；</li>
	 * </ul>
	 * 
	 * @param text 字符串
	 * @return 编码后结果
	 */
	public static String encodeUrl(String text) {
		return text != null ? UriUtils.encodeFragment(text, StandardCharsets.UTF_8) : null;
	}

	/**
	 * 访问URL追加请求参数
	 * 
	 * @param url          访问URL
	 * @param params       请求参数
	 * @param ignoreIfNull 是否或略{@code null}值参数
	 * @return 完整URL
	 */
	public static String appendUrlParams(String url, Map<String, Object> params, boolean ignoreIfNull) {
		StringBuilder sb = new StringBuilder(128);
		boolean first = false;
		if (sb != null) {
			sb.append(url);
		}
		return appendUrlParams(sb, first, params, ignoreIfNull).toString();
	}

	/**
	 * 访问URL上增加访问参数
	 * 
	 * @param sb           字符串构建器，非空。
	 * @param first        是否首个参数
	 * @param params       参数集
	 * @param ignoreIfNull 是否获取空值参数
	 * @return 字符串构建器，非空。
	 */
	protected static StringBuilder appendUrlParams(StringBuilder sb, boolean first, Map<String, Object> params,
			boolean ignoreIfNull) {
		Objects.requireNonNull(sb);
		if (!(params == null || params.isEmpty())) {
			char sp = first ? '?' : '&';
			for (Entry<String, Object> en : params.entrySet()) {
				Object vv = en.getValue();
				if (vv == null && ignoreIfNull) {
					continue;
				}
				sb.append(sp).append(encodeUriComponent(en.getKey())).append("=");
				if ('?' == sp) {
					sp = '&';
				}
				if (vv instanceof CharSequence) {
					sb.append(encodeUriComponent(vv.toString()));
				} else if (vv instanceof Number || vv instanceof Boolean) {
					sb.append(vv.toString());
				} else if (vv instanceof Collection) {

				} else if (vv instanceof Map) {

				} else if (vv != null) {
					if (vv.getClass().isArray()) {
						int len = Array.getLength(vv);
						for (int ii = 0; ii < len; ii++) {
							if (ii != 0) {
//								sb.append(encodeUriComponent(","));
								sb.append(',');
							}
							sb.append(encodeUriComponent(Array.get(vv, ii).toString()));
						}
					} else if (vv.getClass() == java.util.Date.class) {
						String tv = new java.sql.Date(((java.util.Date) vv).getTime()).toString();
						sb.append(encodeUriComponent(tv.replaceAll("\\-", "")));
					} else {
						sb.append(encodeUriComponent(vv.toString()));
					}
				}
			}
		}
		return sb;
	}

	private static final Pattern uriKeyPattern = Pattern.compile("\\{([^}]*)\\}");

	public static String genurl(String path, Map<String, ?> vars) throws HttpUrlException {
		if (path == null || path.length() == 0) {
			return "";
		}
		Matcher m = uriKeyPattern.matcher(path);
		int start = 0;
		StringBuilder sb = null;
		HashSet<String> miss = null;
		while (m.find()) {
			if (null == sb) {
				sb = new StringBuilder(256);
				if (vars == null) {
					vars = Collections.emptyMap();
				}
			}
			String kk = m.group(1);
			Object vv = vars.get(kk);
			if (null == vv) {
				if (null == miss) {
					miss = new HashSet<>();
				}
				miss.add(kk);
			} else {
				sb.append(path, start, m.start());
				sb.append(vv);
				start = m.end();
			}
		}

		String uri = 0 == start ? path : sb.append(path, start, path.length()).toString();
		if (null == miss) {
			return uri;
		}
		throw new HttpUrlException(uri, miss);
	}

	public static HttpHeaders genheaders(Map<String, ?> fields) {
		HttpHeaders headers = new HttpHeaders();
		if (fields != null) {
			for (Entry<String, ?> entry : fields.entrySet()) {
				String vv = Objects.toString(entry.getValue(), "");
				if (vv.length() != 0) {
					headers.set(entry.getKey(), vv);
				}
			}
		}
		return headers;
	}

	public static HttpHeaders headers(String k1, Object v1) {
		HttpHeaders rs = new HttpHeaders();
		rs.set(k1, Objects.toString(v1, ""));
		return rs;
	}

	public static HttpHeaders headers(String k1, Object v1, String k2, Object v2) {
		HttpHeaders rs = new HttpHeaders();
		rs.set(k1, Objects.toString(v1, ""));
		rs.set(k2, Objects.toString(v2, ""));
		return rs;
	}

	public static HttpHeaders headers(String k1, Object v1, String k2, Object v2, String k3, Object v3) {
		HttpHeaders rs = new HttpHeaders();
		rs.set(k1, Objects.toString(v1, ""));
		rs.set(k2, Objects.toString(v2, ""));
		rs.set(k3, Objects.toString(v3, ""));
		return rs;
	}

	public static HttpHeaders headers(String k1, Object v1, String k2, Object v2, String k3, Object v3, String k4,
			Object v4) {
		HttpHeaders rs = new HttpHeaders();
		rs.set(k1, Objects.toString(v1, ""));
		rs.set(k2, Objects.toString(v2, ""));
		rs.set(k3, Objects.toString(v3, ""));
		rs.set(k4, Objects.toString(v4, ""));
		return rs;
	}

	public static HttpHeaders headers(String k1, Object v1, String k2, Object v2, String k3, Object v3, String k4,
			Object v4, String k5, Object v5) {
		HttpHeaders rs = new HttpHeaders();
		rs.set(k1, Objects.toString(v1, ""));
		rs.set(k2, Objects.toString(v2, ""));
		rs.set(k3, Objects.toString(v3, ""));
		rs.set(k4, Objects.toString(v4, ""));
		rs.set(k5, Objects.toString(v5, ""));
		return rs;
	}

}
